/**
 * @file previewValue
 * @author  fanxl
 */
import {
    forEach,
    set,
    fill,
    get,
    isString,
    isObject,
    keys,
    isArray,
    isEmpty,
    map,
    isBoolean,
    hasIn,
    toUpper
} from 'lodash-es';
import type {UnionSpecItem, UploadSpecItem, CustomObject} from '../interface';
import dayjs from 'dayjs';
import {collectExpressions} from './index';
import {getFieldPath} from '../path';

const MAX_PREVIEW_COMPUTED_LEN = 20;

function getPreviewValueByStringSpecItem(spec: UnionSpecItem) {
    const {displayName, ui, rules = {}} = spec;
    if (!rules.required) {
        return '';
    }
    if (ui === 'DatePicker') {
        const {format} = rules.date || {};
        return dayjs().format(format || 'YYYY-MM-DD');
    }
    if (rules.telephone || rules.fax || rules.businessPhone || rules.mobilephone || rules.phone) {
        return '0000-00000000';
    }
    if (rules.link) {
        return 'https://';
    }

    if (rules.email || rules.imageUrl || rules.flvUrl || rules.flashUrl || rules.videoUrl) {
        return '';
    }
    if (rules.int || rules.number) {
        return '0';
    }
    return `请填写${displayName}`;
}
function getPreviewValueByUploadSpecItem(spec: UploadSpecItem) {
    const {extraAttr = {}, rules = {}} = spec;
    if (!rules.required) {
        return '';
    }

    // 默认预览图
    let previewValue = '';

    const {
        width,
        minWidth,
        maxWidth,
        height,
        minHeight,
        maxHeight,
        ratio = '',
        multiple,
        maxCount = MAX_PREVIEW_COMPUTED_LEN
    } = extraAttr;

    const imageWidth = width || minWidth || maxWidth;
    const imageHeight = height || minHeight || maxHeight;
    // 首先，有宽高使用宽高构造图片
    // 其次，有宽高比使用宽高比构造图片
    if (imageWidth && imageHeight) {
        previewValue = `https://dummyimage.com/${imageWidth}x${imageHeight}.jpg`;
    } else if (ratio) {
        let ratioWidth;
        let ratioHeight;
        [ratioWidth, ratioHeight] = ratio.split(':').map((it) => Number(it));
        if (ratioWidth && ratioHeight) {
            // 已100为界计算放大倍数（即返回一个尺寸在100附近的图片），超过100，不放大
            const scale = Math.floor(100 / Math.max(ratioWidth, ratioHeight)) + 1;
            ratioWidth = scale * ratioWidth;
            ratioHeight = scale * ratioHeight;
            previewValue = `https://dummyimage.com/${ratioWidth}x${ratioHeight}.jpg`;
        }
    }
    if (multiple) {
        const multiPreviewValue: string[] = [];
        multiPreviewValue.length = maxCount;
        fill(multiPreviewValue, previewValue);
        return multiPreviewValue;
    }
    return previewValue;
}

function getPreviewValueBySpecItem(spec: UnionSpecItem, data: any = {}, prefix: string = '', initValues: any = {}) {
    const {dataType = '', name, rules = {}, ui, defaultValue} = spec;
    const path = prefix ? `${prefix}.${name}` : name;
    const computedInitValue = get(initValues, name) || defaultValue;

    switch (spec.dataType) {
        case 'STRING':
            set(data, path, computedInitValue || getPreviewValueByStringSpecItem(spec));
            break;
        case 'UPLOAD':
            set(data, path, computedInitValue || getPreviewValueByUploadSpecItem(spec));
            break;
        case 'COLOR':
            if (rules.required) {
                set(data, path, computedInitValue || '#ffffff');
            } else {
                set(data, path, computedInitValue || '');
            }
            break;
        case 'VIDEO':
            set(data, path, computedInitValue || '');
            break;
        case 'AUDIO':
            set(data, path, computedInitValue || '');
            break;
        case 'HTML':
            set(data, path, computedInitValue || getPreviewValueByStringSpecItem(spec));
            break;
        case 'URL':
            if (rules.required) {
                set(data, path, computedInitValue || 'https://');
            } else {
                set(data, path, computedInitValue || '');
            }
            break;
        case 'BOOL':
            const boolValue = get(initValues, name);
            set(data, path, isBoolean(boolValue) ? boolValue : defaultValue === 'true');
            break;
        case 'NUMBER':
            set(data, path, computedInitValue ? +computedInitValue : 0);
            break;
        case 'ENUM':
            const isMulti = ui === 'CheckboxGroup';
            if (rules.required) {
                const firstValue: string | undefined = get(spec, 'enumValues.0.value');
                const mockValue = computedInitValue || firstValue;
                if (spec.enumValues) {
                    set(data, path, isMulti ? [mockValue] : mockValue);
                }
            } else {
                set(
                    data,
                    path,
                    isMulti
                        ? computedInitValue
                            ? [computedInitValue]
                            : []
                        : computedInitValue
                          ? computedInitValue
                          : null
                );
            }
            break;
        case 'OBJECT':
            const children = spec.items;
            set(data, path, {});
            if (children?.length) {
                getPreviewValueWithoutExpressions(children, data, path, computedInitValue);
            }
            break;
        case 'LIST':
            const {items, element, extraAttr} = spec;
            const initialCount = get(extraAttr, 'initialCount');
            const count =
                get(computedInitValue, 'length') ||
                rules.count ||
                initialCount ||
                rules.minCount ||
                rules.maxCount ||
                MAX_PREVIEW_COMPUTED_LEN;
            let arrValue: any[] = [];
            arrValue.length = count;
            if (isArray(computedInitValue) && isEmpty(computedInitValue)) {
                set(data, path, []);
                return;
            }
            if (element) {
                arrValue = map(arrValue, (item: any, index: number) => {
                    const elementPreviewValue: {element?: any} = {};
                    getPreviewValueBySpecItem(
                        {
                            ...element,
                            name: 'element'
                        },
                        elementPreviewValue,
                        '',
                        {element: get(computedInitValue, index)}
                    );
                    return elementPreviewValue.element;
                });
                set(data, path, arrValue);
            } else if (items && items.length) {
                arrValue = map(arrValue, (item: any, index: number) => {
                    const childValue: CustomObject = {};
                    getPreviewValueWithoutExpressions(items, childValue, '', get(computedInitValue, index));
                    return childValue;
                });
                set(data, path, arrValue);
            } else {
                set(data, path, []);
            }
            break;
        case 'ALTERNATIVE':
            const {enumValues} = spec;
            const firstValue: string | undefined = get(enumValues, '0.value');
            let mockValue: string | undefined = '';
            if (isString(computedInitValue)) {
                mockValue = defaultValue || firstValue;
            } else if (isObject(computedInitValue)) {
                mockValue = get(keys(computedInitValue), '0') || firstValue;
            }
            if (enumValues) {
                forEach(enumValues, ({value, items}) => {
                    if (mockValue !== value) {
                        return;
                    }
                    const alterValue = {};
                    set(data, `${path}.${value}`, {});
                    if (items) {
                        getPreviewValueWithoutExpressions(items, alterValue, '', get(initValues, `${name}.${value}`));
                    }
                    set(data, `${path}.${value}`, alterValue);
                });
            }
            break;
        default:
            throw new Error(`dataType [${dataType}] is unknown!`);
    }
}

export function getPreviewValueWithoutExpressions(
    spec: UnionSpecItem | UnionSpecItem[],
    data: any = {},
    prefix: string = '',
    initValues: any = {}
) {
    if (!spec) {
        return {};
    }
    const specArr: UnionSpecItem[] = ([] as UnionSpecItem[]).concat(spec);
    forEach(specArr, (specItem) => {
        getPreviewValueBySpecItem(specItem, data, prefix, initValues);
    });
    return data;
}

export function getPreviewValue(
    spec: UnionSpecItem | UnionSpecItem[],
    data: any = {},
    prefix: string = '',
    initValues: any = {}
) {
    if (!spec) {
        return {};
    }
    const specArr: UnionSpecItem[] = ([] as UnionSpecItem[]).concat(spec);
    const result = getPreviewValueWithoutExpressions(specArr, data, prefix, initValues);
    if (!result) {
        return {};
    }
    const expressionMap = collectExpressions(specArr);

    const keys = Object.keys(expressionMap);

    const processExpression = () => {
        for (let i = 0; i < keys.length; i++) {
            const key = keys[i];
            // 判断path是否在当前数据中存在
            if (!hasIn(result, key)) {
                continue;
            }
            const {removeOn, spec} = expressionMap[key];

            const keyArr: string[] = key.split('.');
            keyArr.pop();

            const parentKey = keyArr.join('.');
            const parentValue = parentKey ? get(result, parentKey) : result;

            const expressionResult = removeOn(result, parentValue, key, get, getFieldPath);

            if (parentValue && expressionResult) {
                try {
                    delete parentValue[spec.name];
                } catch (e) {
                    // eslint-disable-next-line no-console
                    console.log(e);
                }
            }
        }
    };
    processExpression();
    return result;
}
